#ifndef _MATRIX_2D_H_
#define _MATRIX_2D_H_

#include "./matrix_1d.h"

namespace Mathematics {
	namespace Matrix {
		
		//! Class for manipulating ordered sets of ordered double numbers.
		/*!
			A simple Matrix with two dimensions.
			Especially defined for freely manipulate values, sets and sub-sets.
		*/
		class Matrix2D: public Wtb {
		protected:
			
				// -attributes-
			/*! Integer cointaing the unique identifier in the class. */
			usint id;
			/*! Flag indicating if this is an alias for memory region. */
			bool copied;
			
			/*! Matrix dimension. Number of Matrix1D. */
			usint dim_lin;
			/*! Matrix dimension. Dimension of the values. */
			usint dim_col;
			/*! Pointer for the matrix values. */
			Matrix1D **value;
			
				// -static attributes-
			/*! Integer cointaing the next available identifier for class objects. */
			static usint next_id;
			
				// -static private methods-
			static inline usint assignId(){ return next_id++; };
			static inline usint restartId(){ return next_id = 0; };
		
				// -private methods-
			//! copies each value
			void copy(const Matrix2D &original);
			//! mallocs the value
			bool create();
			//! frees the value
			void free();
		
			//! hidden builder for creating alias
			Matrix2D();
			//! hidden builder for creating alias
			Matrix2D(const usint &line_dimension);
			
		public:
			
				// -constructors-
			/*! 
				Constructor
				
				\param line_dimension The quantity of Matrix1D.
				\param column_dimension The quantity of values in each Matrix1D.
				\code
				Matrix2D mat(5,3);	// creates a Matrix2D with 5 Matrix1D with 3 positions.
				\endcode
			*/
			Matrix2D(const usint &line_dimension, const usint &column_dimension);
			/*! 
				Copy Constructor
				
				\param original The object to be copied.
				\sa Matrix2D::Matrix2D(const usint &line_dimension, const usint &column_dimension)
				
				\code
				Matrix2D mat(5,3);	// creates a Matrix2D with 5 Matrix1D with 3 positions.
				// ...
				Matrix2D copy( mat );	// creates a new Matrix with the same dimension and values.
				\endcode
			*/
			Matrix2D(const Matrix2D &original);
			/*! 
				Copy Constructor.
				Used for coercion from Matrix1D.
				
				\param original The object to be copied.
				\sa Matrix1D::Matrix1D(const usint &dimension)
				
				\code
				Matrix1D mat(5);	// creates a Matrix1D with 5 positions.
				// ...
				Matrix2D copy( mat );	// creates a Matrix2D with 5 Matrix1D with 1 position, and copies each value.
				\endcode
			*/
			Matrix2D(const Matrix1D &original);
			/*! 
				Destructor
			*/
			~Matrix2D();
		
				// -getters & setters-
			//! getter for the id
			inline usint getID() const { return id; };
			//! getter for the copy flag
			inline usint getCopied() const { return copied; };
			//! getter for the line dimension value
			inline usint getDimLin() const { return dim_lin; };
			//! getter for the column dimension value
			inline usint getDimCol() const { return dim_col; };
			/*! 
				Method for getting values in the matrix
				
				\param line_index Index line of the desired value.
				\param column_index Index column of the desired value.
				\return copy of the desired value.
				\sa void Matrix2D::setValue(const usint &line_index, const usint &column_index, const double &item)
				
				\code
				Matrix2D mat(5,2);
				mat.setValue(0, 0, 5.0);
				cout << mat.getValue(0,0) << endl; // prints 5.0
				\endcode
			*/
			inline double getValue(const usint &line_index, const usint &column_index) const {
				if(line_index < 0 || line_index >= dim_lin){
					throw OutOfBoundsException("Invalid access",dim_lin,line_index);
				}
				return value[line_index]->getValue(column_index);
			};
			/*! 
				Method for changing values in the matrix
				
				\param line_index Index line of the desired position.
				\param column_index Index column of the desired position.
				\param item The desired new value.
				\sa double Matrix1D::getValue(const usint &line_index, const usint &column_index)const
				
				\code
				Matrix2D mat(5,2);
				mat.setValue(0, 0, 5.0);
				cout << mat.getValue(0,0) << endl; // prints 5.0
				\endcode
			*/
			inline void setValue(const usint &line_index, const usint &column_index, const double &item){
				if(line_index < 0 || line_index >= dim_lin){
					throw OutOfBoundsException("Invalid access",dim_lin,line_index);
				}
				value[line_index]->setValue(column_index,item);
			};
			/*! 
				Method for getting chunks of the matrix
				
				\param line_index Index of the first line value.
				\param column_index Index of the first column value.
				\param line_length Size of the line in the desired Matrix piece.
				\param column_length Size of the column in the desired Matrix piece.
				\return copy of the desired area in a new Matrix
				\sa Matrix2D::operator()(const usint &begin_index, const usint &end_index)
				
				\code
				Matrix2D mat(3,3);
				mat,
					1, 2, 3, nl,
					4, 5, 6, nl,
					7, 8, 9;
				Matrix2D part(2,2);
				part = mat.getSelection(1,1,2,2); // part has now the value [ [ 5 6 ] [ 8 9 ] ]
				\endcode
			*/
			Matrix2D getSelection(const usint &line_index, const usint &column_index,
				const usint &line_length, const usint &column_length) const ;
			/*! 
				Method for setting chunks of the matrix
				Can throw OutOfBoundsException if exceds the original matrix
				
				\param line_index Index of the first line value.
				\param column_index Index of the first column value.
				\param part Matrix piece desired to place.
				\sa Matrix2D::operator()(const usint &begin_index, const usint &end_index)
				
				\code
				Matrix2D mat(3,3);
				mat,
					1, 2, 3, nl,
					4, 5, 6, nl,
					7, 8, 9;
				Matrix2D part(2,2);
				part,
					0, 0, nl,
					0, 0;
				mat.setSelection(1,1,part); // mat has now the value [ [ 1 2 3 ] [ 4 0 0 ] [ 7 0 0 ] ]
				\endcode
			*/
			void setSelection(const usint &line_index, const usint &column_index,
				const Matrix2D &part);
			
				// -operators-
			/*! 
				Operator for transponding square matrix in her self area.
				Can throw OutOfBoundsException if not square matrix.
				
				\return reference to the own matrix.
				\sa Matrix2D::transp()const
				
				\code
				Matrix2D part(3,3);
				part,
					1, 2, 3, nl,
					4, 5, 6, nl,
					7, 8, 9;
				cout << --mat << endl; // prints [ [ 1 4 7 ] [ 2 5 8 ] [ 3 6 9 ] ]
				\endcode
			*/
			Matrix2D& operator -- ();
			/*! 
				Matrix attribution
				
				\param mat Matrix desired to attribution.
				
				\code
				Matrix2D copy(2,3), alias(2,3), mat(2,3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				alias = copy = mat; // alias and copy have now the value [ [ 1 2 3 ] [ 4 5 6 ] ]
				\endcode
			*/
			Matrix2D& operator = (const Matrix2D &mat);
			/*! 
				Matrix sum
				
				\param mat Matrix desired to sum.
				\sa Matrix2D::operator+=(const Matrix2D &mat)
				
				\code
				Matrix2D mat(3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				cout << mat + mat << endl; // prints [ [ 2 4 6 ] [ 8 10 12 ] ]
				\endcode
			*/
			Matrix2D operator + (const Matrix2D &mat) const;
			/*! 
				Matrix sum with left attribution
				
				\param mat Matrix desired to sum.
				\sa Matrix2D::operator=(const Matrix2D &mat)
				\sa Matrix2D::operator+(const Matrix2D &mat)const
				
				\code
				Matrix2D atr(2,3), mat(2,3);
				atr,
					4, 5, 6, nl,
					1, 2, 3;
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				atr += mat; // atr has now the value [ [ 1 2 3 ] [ 4 5 6 ] ]
				\endcode
			*/
			Matrix2D& operator += (const Matrix2D &mat);
			/*! 
				Matrix subtraction
				
				\param mat Matrix desired to subtract.
				\sa Matrix2D::operator-=(const Matrix2D &mat)
				
				\code
				Matrix2D mat(2,3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				cout << mat - mat << endl; // prints [ [ 0 0 0 ] [ 0 0 0 ] ], pretty dumb!
				\endcode
			*/
			Matrix2D operator - (const Matrix2D &mat) const;
			/*! 
				Matrix subtraction with left attribution
				
				\param mat Matrix desired to subtract.
				\sa Matrix2D::operator=(const Matrix2D &mat)
				\sa Matrix2D::operator-(const Matrix2D &mat)const
				
				\code
				Matrix2D atr(2,3), mat(2,3);
				atr,
					4, 5, 6, nl,
					1, 2, 3;
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				atr -= mat; // atr has now the value [ [ 3 3 3 ] [ -3 -3 -3 ] ]
				\endcode
			*/
			Matrix2D& operator -= (const Matrix2D &mat);
			/*! 
				Matrices internal product.
				
				\param mat Matrix desired for the internal product
				\sa Matrix2D::operator--()
				
				\code
				Matrix2D mx(2,2), mat(2,3);
				mx,
					1,	2,	nl,
					2, 1;
				mat,
					1, 0, 3, nl,
					0, 2, 0;
				cout << mx * mat << endl; // prints [ [ 1 4 3 ] [ 2 2 6 ] ]
				\endcode
			*/
			Matrix2D operator * (const Matrix2D &mat) const;
			/*! 
				Matrix multiplication by number
				
				\param scalar number desired to multiply the Matrix.
				\sa Matrix2D Matrix2D::operator*=(const double &scalar)
				
				\code
				Matrix2D mat(3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				cout << mat * 4 << endl; // prints [ [ 4 8 12 ] [ 16 20 24 ] ]
				\endcode
			*/
			Matrix2D operator * (const double &scalar) const;
			/*! 
				Matrix multiplication by number
				
				\param scalar number desired to multiply the Matrix.
				\sa Matrix2D Matrix2D::operator*(const double &scalar)
				
				\code
				Matrix2D mat(3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				cout << 4 * mat << endl; // prints [ [ 4 8 12 ] [ 16 20 24 ] ]
				\endcode
			*/
			friend inline Matrix2D operator * ( const double &scalar, const Matrix2D &mat ){
				return mat*scalar;
			};
			/*! 
				Matrices internal product. It gives the capacity to use any order.
				( watch out for the dimensions! )
				
				\param mat1 left Matrix desired for the internal product (this one will be turned into a Matrix2D)
				\param mat2 right Matrix desired for the internal product
				\sa Matrix2D Matrix2D::operator*(const Matrix2D &mat)const
			*/
			friend inline Matrix2D operator * (const Matrix1D &mat1, const Matrix2D &mat2) {
				return Matrix2D( mat1 ) * mat2;
			};
		
			/*! 
				Matrix multiplication by number with left attribution
				
				\param scalar number desired to multiply the Matrix.
				\sa Matrix2D& Matrix2D::operator=(const Matrix2D &mat)
				\sa Matrix2D Matrix2D::operator*(const double &scalar)const
				
				\code
				Matrix2D mat(2,3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				atr *= 2; // mat has now the value [ [ 2 4 6 ] [ 8 10 12 ] ]
				\endcode
			*/
			Matrix2D& operator *= (const double &scalar);
			/*! 
				Matrix division by number
				Can throw DivisionByZeroException.
				
				\param scalar number desired to divide the Matrix. If zero Throws DivisionByZeroException.
				\sa Matrix2D Matrix2D::operator/=(const double &scalar)
				\sa class DivisionByZeroException
				
				\code
				Matrix1D mat(2,3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				cout << mat / 5 << endl; // prints [ [ 0.2 0.4 0.6 ] [ 0.8 1.0 1.2 ] ]
				\endcode
			*/
			Matrix2D operator / (const double &scalar) const;
			/*! 
				Matrix division by number with left attribution.
				Can throw DivisionByZeroException.
				
				\param scalar number desired to divide the Matrix. If zero Throws DivisionByZeroException.
				\sa Matrix2D& Matrix2D::operator=(const Matrix2D &mat)
				\sa Matrix2D Matrix2D::operator/(const double &scalar)const
				\sa class DivisionByZeroException
				
				\code
				Matrix1D mat(2,3);
				mat,
					1, 2, 3, nl,
					4, 5, 6;
				atr /= 2.0; // mat has now the value [ [ 0.5 1.0 1.5 ] [ 2.0 2.5 3.0 ] ]
				\endcode
			*/
			Matrix2D& operator /= (const double &scalar);
			/*! 
				Operator for manipulating chunks of a Matrix.
				Rips the matrix' lines.
				
				\param begin_index Index of the first value.
				\param end_index Index of the final value (including).
				\return a reference to the desired area. It can work as a Matrix too.
				\sa Matrix2D::getSelection(const usint &line_index, const usint &column_index, const usint &line_length, const usint &column_length)const
				
				\code
				Matrix2D mat(2,4);
				mat,
					1, 2, 3, 4, nl,
					5, 6, 7, 8;
				cout << mat(1,2) << endl; // prints [ [ 2 3 ] [ 6 7 ] ]
				mat(1,3),
					pr, 0, nl,
					0, 0;
				cout << mat(1,3) << endl; // prints [ [ 2 0 4 ] [ 0 0 8 ] ]
				\endcode
			*/
			Matrix2D& operator () (const usint &begin_index, const usint &end_index) ;
			/*! 
				Operator for inserting values in order in the Matrix
				
				\param num value to be inserted in the first index of the Matrix.
				\return a reference to the rest. It can work as a Matrix too. You can keep using this operator.
				\sa Matrix2D::operator()(const usint &begin_index, const usint &end_index)
				
				\code
				Matrix2D mat(2,4);
				mat,
					1, 2, 3, 4, nl,
					5, 6, 7, 8;
				cout << mat << endl; // prints [ [ 1 2 3 4 ] [ 5 6 7 8 ] ]
				cout << ( mat, 9, 0 ) << endl; // prints [ [ 3 4 ] [ 5 6 7 8 ] ] and mat has value [ [ 9 0 3 4 ] [ 5 6 7 8 ] ]
				\endcode
			*/
			Matrix2D& operator , (const double &num) ;
			/*! 
				Operator for doing actions in insertion order in the Matrix
				
				\param action some action to be made.
				\return a reference to the rest. It can work as a Matrix too. You can keep using this operator.
				\sa Matrix2D::operator,(const double &num)
				
				\code
				Matrix2D mat(2,4);
				mat,
					1, 2, 3, 4, nl,
					5, 6, 7, 8;			// mat has now value [ [ 1 2 3 4 ] [ 5 6 7 8 ] ]
				mat,
					pr, pr, 0, nl,
					0, pr, 0;			// mat has now value [ [ 1 2 0 4 ] [ 0 6 0 8 ] ]
				\endcode
			*/
			Matrix2D& operator , (const MatrixManipulation &action) ;
			/*! 
				Operator for accessing individual elements in the Matrix.
				Elements in Matrix2D are Matrix1D
				
				\param index position to be accessed int the Matrix.
				\return the value in that position. It's a Matrix1D, so one can use again the operator[].
				
				\code
				Matrix2D mat(2,4);
				mat,
					1, 2, 3, 4, nl,
					5, 6, 7, 8;
				cout << mat << endl;		// prints [ [ 1 2 3 4 ] [ 5 6 7 8 ] ]
				cout << mat[1] << endl;		// prints [ 5 6 7 8 ]
				cout << mat[1][1] << endl;		// prints 6
				\endcode
			*/
			Matrix1D& operator [] (const usint &index) const ;
			
			bool operator == ( const Matrix2D &matrix ) const ;
			
			inline bool operator != ( const Matrix2D &matrix ) const {
				return !( (*this) == matrix );
			};
			
			/*! 
				Method for transponding matrix.
				
				\return a new Matrix with the result.
				\sa Matrix2D::operator--()const
				
				\code
				Matrix2D mat(2,4);
				mat,
					1, 2, 3, 4, nl,
					5, 6, 7, 8;
				cout << mat.transp() << endl; // prints [ [ 1 5 ] [ 2 6 ] [ 3 7 ] [ 4 8 ] ]
				\endcode
			*/
			Matrix2D transp() const ;
		
				// -read & write-
			/*! 
				Method for converting the Matrix in a string representation
				
				\return a string that represents the Matrix.
				
				\code
				Matrix2D mat(2,4);
				mat,
					1, 2, 3, 4, nl,
					5, 6, 7, 8;
				cout << mat.toS() << endl;		// prints [ [ 1 2 3 4 ] [ 5 6 7 8 ] ]
				\endcode
			*/
			virtual string toS() const ;
			/*! 
				Writes the value of the Matrix in an opened file.
				
				\param parq Pointer to the opened file.
				\return boolean indicating failure or success of the operation
				
				\sa string Matrix2D::toS()const
				
				\code
				Matrix2D mat(2,4);
				mat,
					1, 2, 3, 4, nl,
					5, 6, 7, 8;
				mat.filePrint();		// prints [ [ 1 2 3 4 ] [ 5 6 7 8 ] ] on the terminal
				\endcode
			*/
			virtual bool filePrint( FILE *parq = stdin ) const ;
			
			/*! 
			*/
			double* toPointer() const ;
			/*! 
			*/
			double** toDoublePointer() const ;
			
				// -image-
			
				// -test-
			
		};
		
		typedef IncompatibleParametersException<Matrix2D,Matrix2D> IncompatibleMatrices2DException;

	};
};

#endif // _MATRIX_2D_H_
